package com.zynn.service.module.es.service.impl;

import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.zynn.common.core.utils.DateUtils;
import com.zynn.common.pojo.dto.EventDTO;
import com.zynn.service.module.es.dao.EventsMapper;
import com.zynn.service.module.es.entity.Events;
import com.zynn.service.module.es.service.EventsService;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 刘天元
 * @since 2018-07-01
 */
@Service
public class EventsServiceImpl extends ServiceImpl<EventsMapper, Events> implements EventsService {

    @Autowired
    private RestHighLevelClient client;

    /**
     * 默认的分页数量
     */
    private final Integer PAGE_SIZE = 10;


    /**
     * 工具类:结果集处理
     */
    private static class Result {
        /**
         * 处理
         * group by userId
         * count(total)
         * 结果集
         *
         * @param client
         * @param searchSourceBuilder
         * @return
         */
        private static List<Map<String, Object>> groupByUserIdCountResourceCount(RestHighLevelClient client, SearchSourceBuilder searchSourceBuilder) {
            //1. 明确查询的索引
            SearchRequest searchRequest = new SearchRequest();
            searchRequest.source(searchSourceBuilder);
            searchRequest.indices("event_index");

            List<Map<String, Object>> result = new ArrayList<>(10);
            try {
                System.out.println(searchRequest.toString());
                //2. 执行查询
                SearchResponse searchResponse = client.search(searchRequest);
                //3. 处理分组
                Aggregations aggregations = searchResponse.getAggregations();
                Terms resultGroup = aggregations.get("userId");
                result.addAll(Optional.ofNullable(resultGroup.getBuckets()).orElse(Collections.emptyList())
                        .stream().map(term -> {
                            //4. 处理聚合
                            ParsedSum resultSum = term.getAggregations().get("total");
                            Map<String, Object> map = new HashedMap();
                            map.put(resultSum.getName(), resultSum.getValue());
                            map.put(resultGroup.getName(), term.getKeyAsString());
                            return map;
                        }).collect(Collectors.toList()));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }

        /**
         * 处理
         * List<EventDTO>
         *
         * @param client
         * @param searchSourceBuilder
         * @return
         */
        private static List<EventDTO> eventDTOList(RestHighLevelClient client, SearchSourceBuilder searchSourceBuilder) {
            //1. 明确查询的索引
            SearchRequest searchRequest = new SearchRequest();
            searchRequest.source(searchSourceBuilder);
            searchRequest.indices("event_index");

            List<EventDTO> result = new ArrayList<>();
            try {
                System.out.println(searchRequest.toString());
                //2. 执行查询
                SearchResponse searchResponse = client.search(searchRequest);
                //3. 转换查询的数据
                Arrays.stream(searchResponse.getHits().getHits()).forEach(hit -> result.add(Convert.convertToEventDTO(hit.getSourceAsMap())));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }
    }

    /**
     * 工具类:数据类型转换
     */
    private static class Convert {
        /**
         * 转换数据-将Map对象转换成EventDTO
         *
         * @param source
         * @return
         */
        private static EventDTO convertToEventDTO(Map<String, Object> source) {
            EventDTO event = new EventDTO();
            event.setCommentCount(objectToInteger(source.get("comment_count")));
            event.setLikeCount(objectToInteger(source.get("like_count")));
            event.setResourceCount(objectToInteger(source.get("resource_count")));
            event.setUserId(objectToLong(source.get("user_id")));
            event.setGroupId(objectToLong(source.get("group_id")));
            event.setGroupTypeValue(objectToInteger(source.get("group_type_value")));
            if (StringUtils.isNotEmpty(objectToString(source.get("publish_time")))) {
                event.setPublishTime(DateUtils.parse(objectToString(source.get("publish_time"))));
            }
            event.setLocation((objectToString(source.get("location"))));
            event.setTitle((objectToString(source.get("title"))));
            event.setContent((objectToString(source.get("content"))));
            event.setType(objectToInteger(source.get("event_type")));
            event.setId(objectToLong(source.get("event_id")));
            return event;
        }

        /**
         * 转换数据-将对象转换成Long类型
         *
         * @param object
         * @return
         */
        private static Long objectToLong(Object object) {
            return Long.parseLong(Objects.toString(object, "0"));
        }

        /**
         * 转换数据-将对象转换成Integer类型
         *
         * @param object
         * @return
         */
        private static Integer objectToInteger(Object object) {
            return Integer.parseInt(Objects.toString(object, "0"));
        }

        /**
         * 转换数据-将对象转换成String类型
         *
         * @param object
         * @return
         */
        private static String objectToString(Object object) {
            String str = Objects.toString(object, "");
            String nullStr = "null";
            if (nullStr.equals(str)) {
                str = "";
            }
            return str;
        }
    }


    @Override
    public List<EventDTO> getEvents(Long userId, List<Long> groupIdList, List<Integer> eventTypeList, Long lastEventId, Long pageSize, boolean isAsc, boolean useLimit) {

        //1. 设置es查询基础条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (Objects.nonNull(userId)) {
            boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.termsQuery("user_id", Arrays.asList(userId))));
        }
        if (Objects.nonNull(groupIdList) && !groupIdList.isEmpty()) {
            boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.termsQuery("group_id", groupIdList)));
        }
        if (Objects.nonNull(eventTypeList) && !eventTypeList.isEmpty()) {
            boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.termsQuery("event_type", eventTypeList)));
        }
        if (Objects.nonNull(lastEventId)) {
            RangeQueryBuilder rangeQueryBuilderEventId;

            //1.1 获取最后一条动态的时间,根据时间排序
            String publishTime = getPublishTime(lastEventId);
            if (isAsc) {
                rangeQueryBuilderEventId = QueryBuilders.rangeQuery("publish_time").gt(publishTime);
            } else {
                rangeQueryBuilderEventId = QueryBuilders.rangeQuery("publish_time").lt(publishTime);
            }
            boolQueryBuilder.must(rangeQueryBuilderEventId);
        }

        //2 组装es查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //2.1 组装es基础查询条件
        searchSourceBuilder.query(QueryBuilders.functionScoreQuery(boolQueryBuilder, new RandomScoreFunctionBuilder()));
        //2.2 组装es基础-排序
        searchSourceBuilder.sort("publish_time", isAsc ? SortOrder.ASC : SortOrder.DESC);
        //2.3 组装es基础-分页
        if (useLimit) {
            searchSourceBuilder.size(Objects.nonNull(pageSize) && pageSize > 0L ? pageSize.intValue() : PAGE_SIZE);
        }

        //3. 请求解析结果
        return Result.eventDTOList(client, searchSourceBuilder);
    }

    @Override
    public List<EventDTO> getUserCurrentDayEvents(Long userId, String createTime) {

        //1. 设置es查询基础条件
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publish_time")
                .gt(createTime + " 00:00:00")
                .lt(createTime + " 23:59:59");

        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("user_id", userId);

        //2 组装es基础查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(termQueryBuilder).must(rangeQueryBuilder);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(boolQueryBuilder);

        //3. 请求解析结果
        return Result.eventDTOList(client, searchSourceBuilder);
    }


    /**
     * 获取动态的发布时间
     *
     * @param lastEventId
     * @return
     */
    private String getPublishTime(Long lastEventId) {
        String publishTime = "";
        if (lastEventId != null && lastEventId != 0) {
            GetRequest getRequest = new GetRequest("event_index", "_doc", lastEventId.toString());
            try {
                GetResponse getResponse = client.get(getRequest);
                publishTime = getResponse.getSourceAsMap().get("publish_time").toString();
            } catch (Exception e) {
                publishTime = DateUtils.format(new Date());
            }
        }
        return publishTime;
    }

    @Override
    public EventDTO getEvent(Long eventId) {

        //1. 设置es查询基础条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.termsQuery("event_id", Arrays.asList(eventId))));

        //2 组装es查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //2.1 组装es基础查询条件
        searchSourceBuilder.query(QueryBuilders.functionScoreQuery(boolQueryBuilder, new RandomScoreFunctionBuilder()));

        //3. 请求解析结果
        List<EventDTO> result = Result.eventDTOList(client, searchSourceBuilder);
        return result.isEmpty() ? null : result.get(0);
    }

    @Override
    public List<EventDTO> queryEventByUser(Long userId, Long groupId, Long lastEventId, boolean useLimit) {

        //1. 设置es查询基础条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (Objects.nonNull(userId)) {
            boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.termsQuery("user_id", Arrays.asList(userId))));
        }
        if (Objects.nonNull(groupId)) {
            boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.termsQuery("group_id", Arrays.asList(groupId))));
        }
        if (useLimit && Objects.nonNull(lastEventId)) {
            boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.rangeQuery("event_id").lt(lastEventId)));
        }

        //2 组装es查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //2.1 组装es基础查询条件
        searchSourceBuilder.query(QueryBuilders.functionScoreQuery(boolQueryBuilder, new RandomScoreFunctionBuilder()));
        //2.2 组装es基础-排序
        searchSourceBuilder.sort("publish_time", SortOrder.DESC);
        //2.3 组装es基础-分页
        if (useLimit) {
            searchSourceBuilder.size(PAGE_SIZE);
        }

        //3. 请求解析结果
        return Result.eventDTOList(client, searchSourceBuilder);
    }

    @Override
    public List<EventDTO> getEventsByLikeSort(Long groupId, Integer limit) {

        //1. 设置es查询基础条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (Objects.nonNull(groupId)) {
            boolQueryBuilder.must(QueryBuilders.constantScoreQuery(QueryBuilders.termsQuery("group_id", Arrays.asList(groupId))));
        }

        //2 组装es查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //2.1 组装es基础查询条件
        searchSourceBuilder.query(QueryBuilders.functionScoreQuery(boolQueryBuilder, new RandomScoreFunctionBuilder()));
        //2.2 组装es基础-排序
        searchSourceBuilder.sort("like_count", SortOrder.DESC);
        //2.3 组装es基础-分页
        searchSourceBuilder.size(limit);

        //3.请求解析结果
        return Result.eventDTOList(client, searchSourceBuilder);
    }

    @Override
    public List<Map<String, Object>> getEventsByResourceSort(Long groupId, Integer limit) {

        //1. 设置es查询基础条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termsQuery("group_id", Arrays.asList(groupId)));

        //2. 聚合操作
        //2.1 聚合操作-分組 group by
        TermsAggregationBuilder terms = AggregationBuilders.terms("userId").field("user_id");
        //2.2 聚合操作-sum
        SumAggregationBuilder sum = AggregationBuilders.sum("total").field("resource_count");
        //2.3 聚合操作-group by --> sum
        terms.subAggregation(sum);
        //2.4 聚合操作-排序
        terms.order(BucketOrder.aggregation("total", false));
        //2.5 聚合操作-分页
        terms.size(limit);

        //3 组装es查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //3.1 组装es基础查询条件
        searchSourceBuilder.query(QueryBuilders.functionScoreQuery(boolQueryBuilder, new RandomScoreFunctionBuilder()));
        //3.2 组装es聚合操作
        searchSourceBuilder.aggregation(terms);

        //4.请求解析结果
        return Result.groupByUserIdCountResourceCount(client, searchSourceBuilder);
    }

}
